package edu.fib.upc.erdapfel;

import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.mapsforge.android.maps.MapActivity;
import org.mapsforge.android.maps.MapView;
import org.mapsforge.android.maps.Projection;
import org.mapsforge.android.maps.mapgenerator.tiledownloader.MapnikTileDownloader;
import org.mapsforge.android.maps.overlay.ArrayKMLItemizedOverlay;
import org.mapsforge.android.maps.overlay.ArrayWayOverlay;
import org.mapsforge.android.maps.overlay.GMLLayer;
import org.mapsforge.android.maps.overlay.KMLLayer;
import org.mapsforge.android.maps.overlay.KMLOverlayItem;
import org.mapsforge.android.maps.overlay.Overlay;
import org.mapsforge.core.model.GeoPoint;

import edu.fib.upc.erdapfel.filefilter.FilterByFileExtension;
import edu.fib.upc.erdapfel.filefilter.FilterByFileExtensions;
import edu.fib.upc.erdapfel.filefilter.ValidGmlFile;
import edu.fib.upc.erdapfel.filefilter.ValidKmlFile;
import edu.fib.upc.erdapfel.filefilter.ValidLayerFile;
import edu.fib.upc.erdapfel.filefilter.ValidMapFile;
import edu.fib.upc.erdapfel.filepicker.FilePicker;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.text.format.Time;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.Toast;

public class MainActivity extends MapActivity {

	private class generateMapsforgeMapTask extends AsyncTask<GeoPoint, Integer, String> {	

		@Override
		protected String doInBackground(GeoPoint... points) {

			// TODO: limit the zoom level and all sort of things and warn the user that it's a long process, more easily done in a PC.			

			// call wget or something and store the downloaded archive (asynchronously, of course)
			Log.i(TAG, "Downloading osm data");
			downloadOSMData(points,downloadedOSMFile);

			//this.publishProgress(25);

			// execute the osmosis map-writer plugin and generate a .map of the zone
			Log.i(TAG, "Calling osmosis map-writer");						
			generateMapFromOSM(points,downloadedOSMFile,outFile);

			// TODO: remove temporary files.
			Log.i(TAG, "Task completed.");		

			return returnMessage;			
		}

		@Override
		protected void onPostExecute(String message) {
			alert("Process ended with status: "+message);
			//Toast.makeText(getApplicationContext(), "Process ended with status: " + message, Toast.LENGTH_LONG).show();

			mProgress.setVisibility(View.INVISIBLE );

			((ViewGroup) mProgress.getParent()).removeView(mProgress);

			mapView.invalidate();

			return;
		}

		@Override
		protected void onPreExecute() {				
			mProgress.setVisibility(View.VISIBLE);
			mProgress.bringToFront();

			addContentView(mProgress, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));

			mapView.invalidate();

			return;
		}

		@Override
		protected void onProgressUpdate(Integer... ints) {
			// TODO: Notify the user that the process is being done more accurately
			// Update the progress bar

			return;
		}
	}
	private static final String TAG = "Erdapfel";
	private static final int SELECT_MAP_FILE = 0;
	private static final int SELECT_LAYER_FILE = 1;
	private static final int SELECT_SAVE_FOLDER = 2;
	private MapView mapView;
	private Handler handler = new Handler();
	private File f;
	private int SaveMenuItemId = -1;
	private int LoadMenuItemId = -1;
	private int KMLMenuItemId = -1; 
	private int SaveKMLMenuItemId = -1;
	private List<Overlay> overlays;
	private boolean areaPickerMode = false;
	private boolean polygonDrawMode = false;
	private boolean metricsMode = false;	
	private Projection projection;
	private long duration;
	private int clickCount = 0;
	private long startTime;
	private MetricsOverlay metrics = new MetricsOverlay(projection);
	private AreaPickerOverlay areaPicker = new AreaPickerOverlay();	
	private PolygonDrawOverlay polygonDraw = new PolygonDrawOverlay(projection);

	/* constant for defining the time duration between the click that can be considered as double-tap */
	final static int MAX_DURATION = 500;
	public static final File downloadedOSMFile = new File(Environment.getExternalStorageDirectory()+"/download/"+"tmp.osm");		

	//public static final File inFile = new File(Environment.getExternalStorageDirectory()+"/download/"+"in.osm.pbf");
	public static final File outFile = new File(Environment.getExternalStorageDirectory()+"/download/"+"out.map");
	private static final FileFilter FILE_FILTER_EXTENSION_MAP = new FilterByFileExtension(".map");
	private static final FileFilter FILE_FILTER_EXTENSIONS_LAYERS = new FilterByFileExtensions(Arrays.asList(".kml", ".gml"));
	private static final String KML_FILE_EXTRA_KEY = "KMLFile";
	private static final String MAP_FILE_EXTRA_KEY = "MapFile";

	private static final String KML_BACKTRACK_URL_EXTRA_KEY = "KMLCallBackURL";
	private ProgressBar mProgress;

	private ImageButton mPolygonButton;
	private ImageButton mMetricsButton;
	private ImageButton mClearButton;

	private String returnMessage;
	private Paint polygonPaintOutline;
	private Paint polygonPaintFill;

	private Runnable dissmisser;
	private View uiView;
	private ArrayList<KMLLayer> KMLLayers = new ArrayList<KMLLayer>();

	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {				

		if (this.areaPickerMode) {					
			handleTouchEvent(ev);
		}	
		else if (this.polygonDrawMode){
			if(this.polygonDraw.handleTouchEvent(ev,this.mapView)) 
				this.leavePolygonDrawMode();
		}
		else if (this.metricsMode) {
			this.metrics.handleTouchEvent(ev,this.mapView);					
		}
		else {			
			showUI(); // this tries to emulate the ControlZoom of the mapView	
			return super.dispatchTouchEvent(ev);
		}

		return false;
	}

	public void downloadOSMData(GeoPoint[] points, File downloadedosmfile2) {
		double minLon = Double.MAX_VALUE;
		double minLat = Double.MAX_VALUE;
		double maxLon = Double.MIN_VALUE;
		double maxLat = Double.MIN_VALUE;

		for (GeoPoint p : points ) {
			// openstreetmap api expects a bounding box as a comma separated values 
			// in the order:
			// left, bottom, right, top (min long, min lat, max long, max lat).

			if (p.getLatitude() > maxLat)
				maxLat = p.getLatitude();
			if (p.getLatitude() < minLat)
				minLat = p.getLatitude();
			if (p.getLongitude() > maxLon)
				maxLon = p.getLongitude();
			if (p.getLongitude() < minLon)
				minLon = p.getLongitude();
		}

		// TODO: Check if the area is small enough (The server may reject your region if it
		// is larger than 1/4 degree in either dimension.)			

		// format the URL API request using the selected area
		String url = "http://api.openstreetmap.org/api/0.6/map?bbox="
				+ String.valueOf(minLon) + "," 
				+ String.valueOf(minLat) + "," 
				+ String.valueOf(maxLon) + "," 
				+ String.valueOf(maxLat);

		Log.d(TAG, "Requesting osm data from server with the url: "+url);

		DefaultHttpClient client = new DefaultHttpClient();
		HttpGet httpGet = new HttpGet(url);

		try {
			HttpResponse execute = client.execute(httpGet);
			InputStream content = execute.getEntity().getContent();

			OutputStream out = new FileOutputStream(downloadedOSMFile);

			int read = 0;
			byte[] bytes = new byte[1024];

			while ((read = content.read(bytes)) != -1) {
				out.write(bytes, 0, read);
			}

			content.close();
			out.flush();
			out.close();

		} catch (Exception e) {
			e.printStackTrace();
		}
	}		

	public void generateMapFromOSM(GeoPoint[] points, File inFile, File outFile) {		

		// Setting up the bounding Box for the mapfile-writer plugin
		/*
		 * bounding box definition as comma-separated list of coordinates in the form: 
		 * minLat,minLon,maxLat,maxLon 
		 */
		double minLon = Double.MAX_VALUE;
		double minLat = Double.MAX_VALUE;
		double maxLon = Double.MIN_VALUE;
		double maxLat = Double.MIN_VALUE;

		for (GeoPoint p : points ) {
			if (p.getLatitude() > maxLat)
				maxLat = p.getLatitude();
			if (p.getLatitude() < minLat)
				minLat = p.getLatitude();
			if (p.getLongitude() > maxLon)
				maxLon = p.getLongitude();
			if (p.getLongitude() < minLon)
				minLon = p.getLongitude();	
		}

		/* Plugins to be loaded
		 * org.mapsforge.map.writer.osmosis.MapFileWriterPluginLoader
		 * org.openstreetmap.osmosis.core.CorePluginLoader
		 * org.openstreetmap.osmosis.xml.XmlPluginLoader
		 */					

		String[] args = new String[] {
				"-verbose",
				"-plugin", 
				"org.openstreetmap.osmosis.core.CorePluginLoader",
				"-plugin",
				"org.openstreetmap.osmosis.xml.XmlPluginLoader",
				"-plugin",
				"org.mapsforge.map.writer.osmosis.MapFileWriterPluginLoader",
				"--read-xml", 
				"file="+inFile,
				"--sort",
				"--mapfile-writer", 
				"file="+outFile,
				"bbox="+minLat+","+minLon+","+maxLat+","+maxLon,
				"map-start-position="+(minLat+(maxLat-minLat))+","+(minLon+(maxLon-minLon)),
				"type=hd",				
				"polygon-clipping=false",
				"way-clipping=false",
				"label-position=false",
				"simplification-factor=25"
		};

		String msg = "Executing ARGS: ";
		for (int i=0; i<args.length; i++)
			msg = msg+" "+args[i];		

		Log.d(TAG, msg);

		Log.d(TAG, "Launching edu.fib.upc.mapfile_writer4and");

		try {
			PackageManager pm = getPackageManager();
			Intent intent = pm.getLaunchIntentForPackage("edu.fib.upc.mapfile_writer4and");
			if (intent == null) throw new PackageManager.NameNotFoundException();
			intent.putExtra("args",args);
			startActivity(intent);
		}
		catch (PackageManager.NameNotFoundException n) {
			msg = "Sorry, the conversion from OSM data to map failed because the app " +
					"edu.fib.upc.mapfile_writer4and was not found on the system. Please " +
					"install it, or process the data somewhere else with osmosis tool and " +
					"the mapsforge mapfile-writer plugin.";			
			Log.e(TAG,msg);

			returnMessage = msg;
		}

		return;
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);	

		SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);

		if( pref.getBoolean(SettingsActivity.FULL_SCREEN, false)) {
			requestWindowFeature(Window.FEATURE_NO_TITLE);
			getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 
					WindowManager.LayoutParams.FLAG_FULLSCREEN);
		}

		// Check for input parameters
		// MapFile
		if( this.getIntent().getStringExtra(MAP_FILE_EXTRA_KEY) != null ) {						
			File mapFile = new File(this.getIntent().getStringExtra(MAP_FILE_EXTRA_KEY));
			setMapFileMapContent(mapFile);
		}		
		// NoMapFile, trying to get Internet-maps
		else {		
			this.mapView = new MapView(this, new MapnikTileDownloader());
			this.mapView.setClickable(true);
			this.mapView.setBuiltInZoomControls(true);
		}		
		// KMLFiles
		if( this.getIntent().getStringExtra(KML_FILE_EXTRA_KEY) != null ) {
			File kmlFile = new File(this.getIntent().getStringExtra(KML_FILE_EXTRA_KEY));
			String url = null;
			if(this.getIntent().getStringExtra(KML_BACKTRACK_URL_EXTRA_KEY) != null)
				url = new String(this.getIntent().getStringExtra(KML_BACKTRACK_URL_EXTRA_KEY));
			addKMLFileMapContent(kmlFile,url);			
		}

		LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
		this.uiView = inflater.inflate(R.layout.ui, null);
		this.uiView.setVisibility(View.INVISIBLE);
		
		setContentView(this.mapView);																
		addContentView(this.uiView, new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
		
		initUI();

		// Setup paints and such
		initPaintStyles();
	}	

	private void initUI() {

		this.mProgress = new ProgressBar(this.mapView.getContext(), null, android.R.attr.progressBarStyleHorizontal);
		this.mProgress.setIndeterminate(true);
		this.mProgress.setVisibility(View.INVISIBLE);

		this.mPolygonButton = (ImageButton) findViewById(R.id.ui_polygon_mode_button);				
		this.mPolygonButton.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				enterPolygonDrawMode();
			}			
		});

		this.mMetricsButton = (ImageButton) findViewById(R.id.ui_metrics_mode_button);		
		this.mMetricsButton.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				enterMetricsMode();
			}		
		});						        

		this.mClearButton = (ImageButton) findViewById(R.id.ui_clear_button);		
		this.mClearButton.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				clearLayers();
			}			
		});						        
	}

	@Override
	public void onBackPressed() {
		Log.d(TAG, "onBackPressed Called");
		if(polygonDrawMode) {
			if(polygonDraw != null && polygonDraw.getPoints() != null) {
				if(polygonDraw.getPoints().size() <= 1) {		   
					leavePolygonDrawMode();
				}			   
				else {
					polygonDraw.removeLastWayPoint();
				}
			}
		}
		else if(metricsMode) {
			if(metrics != null && metrics.getPoints() != null) {
				if(metrics.getPoints().size() <= 1) {		   
					leaveMetricsMode();
				}			   
				else {
					metrics.clear();
				}
			}
		}
		else if(areaPickerMode) {
			leaveAreaPickerMode();
		}
		else {
			super.onBackPressed();
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.activity_main, menu);

		// TODO: control if we're offline or online
		MenuItem mi = menu.add(Menu.NONE,0,Menu.NONE,R.string.menu_save_offline_map);		
		SaveMenuItemId  = mi.getItemId();

		mi = menu.add(Menu.NONE,1,Menu.NONE,R.string.menu_load_offline_map);
		LoadMenuItemId = mi.getItemId();

		mi = menu.add(Menu.NONE,2,Menu.NONE,R.string.menu_load_kml_overlay);
		KMLMenuItemId = mi.getItemId();		

		mi = menu.add(Menu.NONE,3,Menu.NONE,R.string.menu_save_kml_overlay);
		SaveKMLMenuItemId = mi.getItemId();

		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle item selection    	
		if(item.getItemId() == SaveMenuItemId) {
			enterAreaPickerMode();
			return true;   
		}
		else if(item.getItemId() == LoadMenuItemId) {
			startMapFilePicker();
			return true;
		}
		else if(item.getItemId() == KMLMenuItemId) {
			startLayerFilePicker();
			return true;
		}
		else if(item.getItemId() == SaveKMLMenuItemId) {
			startKMLFileSaver();
			return true;
		}
		else if(item.getItemId() == R.id.menu_settings) {
			startActivity(new Intent(this, SettingsActivity.class));			
			return true;
		}		
		else {
			return super.onOptionsItemSelected(item);
		}
	}

	public void timerDelayDismissView(long time, final View view) {
		this.handler.removeCallbacks(this.dissmisser);
		this.dissmisser = new Runnable() {           
			public void run() {                
				view.setVisibility(View.GONE);	            
			}
		};		
		this.handler.postDelayed(this.dissmisser, time); 
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
		if (requestCode == SELECT_MAP_FILE) {
			if (resultCode == RESULT_OK) {
				if (intent != null && intent.getStringExtra(FilePicker.SELECTED_FILE) != null && this.mapView != null) {					
					Log.d(TAG,"Offline Map File Accepted");
					setMapFileMapContent(new File(intent.getStringExtra(FilePicker.SELECTED_FILE)));
				}	
			} else if (resultCode == RESULT_CANCELED) {
				Log.d(TAG,"No file selected");
				//finish();
			}
		} else if (requestCode == SELECT_LAYER_FILE) {
			if (resultCode == RESULT_OK) {
				if (intent != null && intent.getStringExtra(FilePicker.SELECTED_FILE) != null && this.mapView != null) {
					Log.d(TAG,"Offline Map Not Accepted assuming KML");
					
					
					try {
						File selected = new File(intent.getStringExtra(FilePicker.SELECTED_FILE));					
						if (new ValidKmlFile().accept(selected)) {
							addKMLFileMapContent(selected,null);
							this.mapView.invalidate();
							//this.setContentView(this.mapView);
							//this.addContentView(this.uiView, new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
						}
						else if (new ValidGmlFile().accept(selected)) {
							addGMLFileMapContent(selected);
							this.mapView.invalidate();
						}
					}
					catch(java.lang.OutOfMemoryError e)
					{
						this.alert("Can't load file due to memory restrictions!");						
						System.gc();
					}					
				}
			} else if (resultCode == RESULT_CANCELED) {
				Log.d(TAG,"No file selected");
				//finish();
			}	
		} else if (requestCode == SELECT_SAVE_FOLDER) {
			// TODO: in case a selected folder prompt for filename in a dialog before saving?
			// TODO: save in the selected folder.
		}
	}

	private void addKMLFileMapContent(File file, String url) {				

		KMLLayer k = new KMLLayer();		
		if (url == null || url.equals("")) 			
			Log.d(TAG, "ACHTUNG! the backtrack URL is not set, we won't be able call the source App.");
		k.setBacktrackURL(url); //k.setBacktrackURL("uni.projecte.zamiaDroid.Citation.EDIT");
		k.setDefaultPolyPaintOutline(this.polygonPaintOutline);
		k.setDefaultPolyPaintFill(this.polygonPaintFill);
		// TODO: find a better way to do this.. is better to force a constructor with drawable parameter?
		k.setDefaultItemMarker(this.getResources().getDrawable(R.drawable.ic_launcher));

		Log.d(TAG, "Trying to set KML FIle to "+file.getAbsolutePath());
		k.setKML(file);

		// add all overlays to the MapView
		this.mapView.getOverlays().addAll(k);			

		// testing if the IDs got through the kml file
		// TODO: this is only working for the Array KML Itemized Overlays
		// FIND A BETTER WAY TO STORE THE ID OF EACH ITEM. Maybe create a dictionary somewhere during the binding?
		for(int i=0; i< k.size(); i++) {
			try {
				if (k.get(i) instanceof ArrayKMLItemizedOverlay) {
					ArrayKMLItemizedOverlay kov = (ArrayKMLItemizedOverlay) k.get(i);
					// getting the first item
					KMLOverlayItem kit = kov.getItem(0);
					Log.d(TAG, "ID of the KML Object: "+kit.getKmlId());
				}
			}
			catch (Exception e) {
				Log.e(TAG, "No ID found in that Overlay: "+e.getCause()+" "+e.getMessage());
				System.gc();
			}
		}

		// Saving the KMLLayer avoids trouble if we want to save the shit later
		this.KMLLayers.add(k);		
	}
	
	private void addGMLFileMapContent(File file) {				

		GMLLayer g = new GMLLayer();						
		g.setDefaultPolyPaintOutline(this.polygonPaintOutline);
		g.setDefaultPolyPaintFill(this.polygonPaintFill);		
		g.setDefaultItemMarker(this.getResources().getDrawable(R.drawable.ic_launcher));

		Log.d(TAG, "Trying to set GML FIle to "+file.getAbsolutePath());
		g.setGML(file);

		// add all overlays to the MapView
		this.mapView.getOverlays().addAll(g);			
	}


	private void enterAreaPickerMode() {

		hideUI();

		if(this.polygonDrawMode)
			this.leavePolygonDrawMode(); // double check

		// Start picker mode
		Log.d(TAG, "Entering Area Picker Mode");		
		this.areaPickerMode = true;

		this.overlays = mapView.getOverlays();

		if (!overlays.contains(areaPicker)) {
			areaPicker = new AreaPickerOverlay();
			overlays.add(areaPicker);
		}		

		this.mapView.invalidate();
	}

	private void enterMetricsMode() {
		hideUI();

		// Start polygon draw mode
		Log.d(TAG, "Entering Metrics Mode");				
		this.metricsMode = true;

		this.overlays = mapView.getOverlays();

		if (!overlays.contains(metrics)) {
			metrics = new MetricsOverlay();
			overlays.add(metrics);
		}		

		this.mapView.invalidate();
	}	

	private void enterPolygonDrawMode() {

		hideUI();

		if(this.areaPickerMode)
			this.leaveAreaPickerMode(); // double check

		// Start polygon draw mode
		Log.d(TAG, "Entering Polygon Draw Mode");				
		this.polygonDrawMode = true;

		this.overlays = mapView.getOverlays();

		if (!overlays.contains(polygonDraw)) {
			polygonDraw = new PolygonDrawOverlay();
			overlays.add(polygonDraw);
		}		

		this.mapView.invalidate();
	}

	private boolean handleTouchEvent(MotionEvent ev) {

		// Handle the pinch not as zoom but as resize selection area
		boolean hasMultiTouch;

		try {
			hasMultiTouch = Build.VERSION.SDK_INT >= 5;
		}
		catch (Exception e) {
			// TODO: specify concrete "too old" exception (no Build.VERSION.SDK:INT) type
			hasMultiTouch = false;
		}
		int c = hasMultiTouch ? WrapNew.getPointerCount(ev) : 1;

		boolean handled = false;

		if(c >= 2) {
			Point points[] = new Point[c];			

			for (int i = 0; i < c; i++)
			{
				// FIXIT: this is API level 5~7 treatment and it seems to be deprecated.
				int x = (int) WrapNew.getX(ev,i);
				int y = (int) WrapNew.getY(ev,i);	
				int id = WrapNew.getPointerId(ev,i);		    				    		
				int action = (ev.getAction() & MotionEvent.ACTION_MASK);				


				switch (action)
				{	            			
				case MotionEvent.ACTION_MOVE:
					// Update
					points[id] = new Point(x,y);					
					break;
				}

			}

			// Update the areaPickerOverlay
			if((points[0] != null) && (points[1] != null)) {
				this.areaPicker.setP1(points[0]);
				this.areaPicker.setP2(points[1]);
				this.areaPicker.requestRedraw();				
			}

			// tell the dispatchEvent that we'v handled this event. Don't pass it down. 
			handled = true;
		}
		else {
			// This is a rude implementation of on Tap event
			switch ((ev.getAction() & MotionEvent.ACTION_MASK)) 
			{
			case MotionEvent.ACTION_DOWN:
				startTime = System.currentTimeMillis();
				clickCount++;
				break;
			case MotionEvent.ACTION_UP:
				long time = System.currentTimeMillis() - startTime;
				duration = duration + time;
				if(clickCount == 2)
				{
					if(duration <= MAX_DURATION)
					{
						Toast.makeText(this.getApplicationContext(), "Area Selected",Toast.LENGTH_SHORT).show();

						Area area = leaveAreaPickerMode();	

						projection = mapView.getProjection();

						// this gets the OnScreen points to GeoPoints
						GeoPoint gP1 = projection.fromPixels(area.p1.x, area.p1.y);
						GeoPoint gP2 = projection.fromPixels(area.p2.x, area.p2.y);	        

						savingProcedure(gP1, gP2);
					}
					clickCount = 0;
					duration = 0;
					break;             
				}				   			
				break;	
			}		
		}

		return handled;
	}

	private void initPaintStyles() {
		// create the default paint objects for overlay ways
		this.polygonPaintFill = new Paint(Paint.ANTI_ALIAS_FLAG);
		this.polygonPaintFill.setStyle(Paint.Style.FILL);
		this.polygonPaintFill.setColor(Color.CYAN);
		this.polygonPaintFill.setAlpha(192);

		this.polygonPaintOutline = new Paint(Paint.ANTI_ALIAS_FLAG);
		this.polygonPaintOutline.setStyle(Paint.Style.STROKE);
		this.polygonPaintOutline.setColor(Color.BLUE);
		this.polygonPaintOutline.setAlpha(128);
		this.polygonPaintOutline.setStrokeWidth(7);
		this.polygonPaintOutline.setStrokeJoin(Paint.Join.ROUND);
	}

	private Area leaveAreaPickerMode() {
		// End picker mode and return the values 
		this.areaPickerMode = false;		
		Log.d(TAG, "Leaving Area Picker Mode");

		Area res = new Area();
		res.setP1(areaPicker.getP1());
		res.setP2(areaPicker.getP2());

		// Remove the areaPicker form the mapView Overlays
		overlays = mapView.getOverlays();		
		overlays.remove(areaPicker);

		this.mapView.invalidate();

		return res;
	}

	private void leaveMetricsMode() {
		this.metricsMode = false;
		Log.d(TAG, "Leaving Metrics Mode");

		this.overlays = mapView.getOverlays();
		this.overlays.remove(this.metrics);

		this.mapView.invalidate();
	}

	private Overlay leavePolygonDrawMode() {
		// End picker mode and return the values 
		this.polygonDrawMode = false;		
		Log.d(TAG, "Leaving Polygon Draw Mode");

		this.overlays = mapView.getOverlays();
		this.overlays.remove(this.polygonDraw);

		if( this.polygonDraw != null && this.polygonDraw.getWay() != null) {
			ArrayWayOverlay Poly = new ArrayWayOverlay(this.polygonPaintFill, this.polygonPaintOutline);
			Poly.addWay(this.polygonDraw.getWay());		

			this.overlays.add(Poly);						
		}

		this.mapView.invalidate();

		return null;		
	}

	private void savingProcedure(GeoPoint gP1, GeoPoint gP2) {
		// TODO ask confirmation?

		// TODO Warn the user of the estimated / available memory consumption		
		Log.d(TAG, "MAX_MEMORY = "+Runtime.getRuntime().maxMemory());

		// Asynchronously call the map writer and whatever it takes.		
		new generateMapsforgeMapTask().execute(gP1,gP2);
	}

	private void setMapFileMapContent(File file) {
		this.f = file;
		boolean saveOverlays = ( this.mapView != null && this.mapView.getOverlays() != null );
		// a offline map file has been selected to open
		List<Overlay> temp = null;
		if ( saveOverlays ) {			
			temp = this.mapView.getOverlays();		
		}
		
		this.mapView = new MapView(this);
		this.mapView.setClickable(true);
		this.mapView.setBuiltInZoomControls(true);
		this.mapView.setMapFile(f);
		this.mapView.getController().setCenter(this.mapView.getMapDatabase().getMapFileInfo().mapCenter);
		if ( saveOverlays )
			this.mapView.getOverlays().addAll(temp);

		this.mapView.invalidate();			

		this.setContentView(mapView);	
		if (this.uiView != null)
			this.addContentView(this.uiView, new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
	}

	private void showUI() {		
		this.uiView.setVisibility(View.VISIBLE);	
		timerDelayDismissView( (long) ViewConfiguration.getZoomControlsTimeout(),this.uiView);
	}

	private void hideUI() {
		this.uiView.setVisibility(View.GONE);		
	}

	/**
	 * Starts the FilePicker to select a kml file.
	 */
	private void startLayerFilePicker() {
		FilePicker.setFileDisplayFilter(FILE_FILTER_EXTENSIONS_LAYERS);
		FilePicker.setFileSelectFilter(new ValidLayerFile());
		//FilePicker.setFileSelectFilter(null);
		startActivityForResult(new Intent(this, FilePicker.class), SELECT_LAYER_FILE);
	}

	/**
	 * Prompts for a folder and a filename to save.
	 */
	private void startKMLFileSaver() {
		SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
		String path = pref.getString(SettingsActivity.SAVING_FOLDER, null);

		if( path != null) {
			// The folder has been determined in the settings
			Log.d(TAG, "Saving KML Layers to: " + path);

			KMLLayer liar = new KMLLayer();

			for(Iterator<Overlay> k = this.mapView.getOverlays().iterator(); k.hasNext();){
				// One File Per Layer or combine the shit?
				// TODO: get the filename pattern regExpr or something from the settings too.
				liar.add(k.next());								
			}

			String s = liar.getKML();

			File dir = new File(path);
			if(!dir.exists()) {
				alert(getResources().getString(R.string.dialog_create_savingfolder)+path);
				// 	Trying to create directories
				dir.mkdirs();
			}
			if(dir.exists() && dir.isDirectory()) {				
				Time time = new Time();				
				time.setToNow();		
				File file = new File (path, "layer_map_" + time.format("%d_%m_%Y_%H_%M_%S") + ".kml");
				if (file.exists ()) file.delete (); 
				try {
					FileOutputStream out = new FileOutputStream(file);
					out.write(s.getBytes());					
					out.flush();
					out.close();

				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			else {

			}
		}
		else {			
			// Something went very wrong while getting the settings... aborting
			Log.e(TAG, getResources().getString(R.string.error_settings_savingfolder));		
			Toast.makeText(getApplicationContext(), getResources().getString(R.string.error_settings_savingfolder), Toast.LENGTH_LONG).show();
			return;

			// TODO: instead of returning ask the user for a folder?
			//FilePicker.setFileSelectFilter(null);	
			//startActivityForResult(new Intent(this, FilePicker.class), SELECT_SAVE_FOLDER);
		}
	}

	private void alert(String string) {		

		AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);

		builder.setMessage(string);
		builder.setTitle("Alert: ");
		builder.setPositiveButton("OK", new OnClickListener() {
			public void onClick(DialogInterface dialog, int which) {
				dialog.cancel();					
			}
		});		

		AlertDialog al = builder.create();

		al.show();		
	}

	/**
	 * Starts the FilePicker to select a file.
	 */
	private void startMapFilePicker() {
		FilePicker.setFileDisplayFilter(FILE_FILTER_EXTENSION_MAP);
		FilePicker.setFileSelectFilter(new ValidMapFile());
		startActivityForResult(new Intent(this, FilePicker.class), SELECT_MAP_FILE);
	}

	private void clearLayers() {
		if( this.mapView != null && this.mapView.getOverlays() != null ) {
			this.mapView.getOverlays().clear();
			if(this.overlays != null)
				this.overlays.clear();
			System.gc();
		}

	}		
}
